#include "types.h"
#include "lib.h"
#include "hwirq.h"
#include "uart.h"
#include "timer.h"
#include "task.h"

#define MCAUSE_MASK_INTERRUPT	(uint64_t)((uint64_t)1 << 63U)
#define MCAUSE_MASK_ECODE		~(uint64_t)((uint64_t)1 << 63U)

/**
 * instr_len() - get instruction length
 *
 * @i:		low 16 bits of the instruction
 * Return:	number of u16 in instruction
 */
static int instr_len(uint16_t i)
{
	if ((i & 0x03) != 0x03)
		return 1;
	/* Instructions with more than 32 bits are not yet specified */
	return 2;
}

/**
 * show_code() - display code leading to exception
 *
 * @epc:	program counter
 */
static void show_code(uint64_t epc)
{
	uint16_t *pos = (uint16_t *)(epc & ~1UL);
	int i, len = instr_len(*pos);

	mprintf("\nCode: ");
	for (i = -8; i; ++i)
		mprintf("%08x ", pos[i]);
	mprintf("(");
	for (i = 0; i < len; ++i)
		mprintf("%08x%s", pos[i], i + 1 == len ? ")\n" : " ");
}


static void show_regs(struct context_t *context)
{
	mprintf("\nSP:  " REG_FMT " GP:  " REG_FMT " TP:  " REG_FMT "\n",
	       context->sp, context->gp, context->tp);
	mprintf("T0:  " REG_FMT " T1:  " REG_FMT " T2:  " REG_FMT "\n",
	       context->t0, context->t1, context->t2);
	mprintf("S0:  " REG_FMT " S1:  " REG_FMT " A0:  " REG_FMT "\n",
	       context->s0, context->s1, context->a0);
	mprintf("A1:  " REG_FMT " A2:  " REG_FMT " A3:  " REG_FMT "\n",
	       context->a1, context->a2, context->a3);
	mprintf("A4:  " REG_FMT " A5:  " REG_FMT " A6:  " REG_FMT "\n",
	       context->a4, context->a5, context->a6);
	mprintf("A7:  " REG_FMT " S2:  " REG_FMT " S3:  " REG_FMT "\n",
	       context->a7, context->s2, context->s3);
	mprintf("S4:  " REG_FMT " S5:  " REG_FMT " S6:  " REG_FMT "\n",
	       context->s4, context->s5, context->s6);
	mprintf("S7:  " REG_FMT " S8:  " REG_FMT " S9:  " REG_FMT "\n",
	       context->s7, context->s8, context->s9);
	mprintf("S10: " REG_FMT " S11: " REG_FMT " T3:  " REG_FMT "\n",
	       context->s10, context->s11, context->t3);
	mprintf("T4:  " REG_FMT " T5:  " REG_FMT " T6:  " REG_FMT "\n",
	       context->t4, context->t5, context->t6);
}

static void _exit_trap(uint64_t code, uint64_t epc, struct context_t *context)
{
	static const char * const exception_code[] = {
		"Instruction address misaligned",
		"Instruction access fault",
		"Illegal instruction",
		"Breakpoint",
		"Load address misaligned",
		"Load access fault",
		"Store/AMO address misaligned",
		"Store/AMO access fault",
		"Environment call from U-mode or VU-mode",
		"Environment call from HS-mode",
		"Environment call from VS-mode"
		"Environment call from M-mode"
		"Instruction page fault",
		"Load page fault",
		"Reserved",
		"Store/AMO page fault",
	};

	mprintf("===============Oops!!==========\n");
	if (code < ARRAY_SIZE(exception_code))
		mprintf("Unhandled exception: %d %s\n", code, exception_code[code]);
	else
		mprintf("Unhandled exception code: %ld\n", code);

	mprintf("EPC: " REG_FMT " RA: " REG_FMT " \n", epc, context->ra);

	show_regs(context);
	show_code(epc);
	// show_efi_loaded_images(epc);
	panic("==========Oops==========\n");
}

static void external_interrupt_handler()
{
	uint64_t hwirq = plic_claim();

	if (hwirq == UART0_IRQ){
		uart_isr();
	} else if (hwirq) {
		mprintf("unexpected interrupt hwirq = %d\n", hwirq);
	}

	if (hwirq) {
		plic_complete(hwirq);
	}
}

static void soft_interrrupt_handler()
{
	/*
		soft interrupts are used here to implement collaborative tasks
	*/
	uint64_t hart = csr_read(mhartid);
	*(uint32_t *)CLINT_MSIP(hart) = 0x0;
	schedule();
}

uint64_t rv_trap_handler(uint64_t epc, uint64_t cause, struct context_t *context)
{
	uint64_t pc = epc;
	uint64_t cause_code = cause & MCAUSE_MASK_ECODE;

	if (cause & MCAUSE_MASK_INTERRUPT) {
		// interrupt
		switch (cause_code) {
			case 3:
				mprintf("[%s:%d]: software interrupt\n", __func__, __LINE__);
				soft_interrrupt_handler();
				break;
			case 7:
				timer_handler();
				break;
			case 11:
				external_interrupt_handler();
				break;
			default:
				mprintf("Unknown async exception! Code = %ld\n", cause_code);
				break;
		}

	} else {
		// exception
		_exit_trap(cause_code, epc, context);
		// return pc+4;
	}

	return	pc;
}


void trap_test()
{

	/*
	 * Synchronous exception code = 7
	 * Store/AMO access fault
	 */
	*(int *)0x0 = 100;


	/*
	 * Synchronous exception code = 5
	 * Load access fault
	 */
	//int a = *(int *)0x0;

	mprintf("Yeah! I'm return back from trap!\n");

}
